Ocular Cataract Intelligent Recognition (OCIR) ¶

Leveraging the power of Deep Learning, our trained Convolutional Neural Network(CNN) model aims to detect one of the most common ocular conditions- Cataract.

Project Team:

  1. Joshua S Raju 22BAI1213
  2. Shubham Yadav 22BAI1427
  3. Md Rameez Haider 22BRS1327

DISCLAIMER: While our CNN model aids in cataract detection, it is not a substitute for professional medical advice. Please consult a qualified healthcare provider for a comprehensive eye examination and diagnosis.

DATASET: https://www.kaggle.com/datasets/andrewmvd/ocular-disease-recognition-odir5k https://www.kaggle.com/datasets/jr2ngb/cataractdataset

Importing Dependencies¶

In [1]:
import numpy as np
import os

import cv2
import matplotlib.pylab as plt
import imghdr

import tensorflow as tf

Tensorflow GPU Management

In [2]:
gpus = tf.config.experimental.list_physical_devices('GPU')
for gpu in gpus:
    tf.config.experimental.set_memory_growth(gpu, True)

Cleaning Image Data¶

In [3]:
data_dir = 'imgdata_v3'

Creating Image Dataset¶

In [4]:
dataset = tf.keras.utils.image_dataset_from_directory(data_dir, image_size=(256, 256))
Found 7437 files belonging to 2 classes.
In [5]:
batch = dataset.as_numpy_iterator().next()
len(batch)
Out[5]:
2
In [6]:
classes = ['cataract', 'normal']
classes
Out[6]:
['cataract', 'normal']
In [7]:
fig, axs = plt.subplots(5,5, figsize=(20,20))
for idx, img in enumerate(batch[0][:25]):
    row = idx//5
    col = idx%5

    axs[row, col].imshow(img.astype(int))
    axs[row, col].axis('off')
    axs[row, col].set_title(classes[batch[1][idx]])
No description has been provided for this image

Scaling Dataset¶

In [8]:
batch[0][0].shape, batch[0][0].max(), batch[0][0].min()
Out[8]:
((256, 256, 3), 254.75, 0.0)
In [9]:
data = dataset.map(lambda x,y: (x/255, y))
In [10]:
print(data.as_numpy_iterator().next()[0][0].shape, 
    data.as_numpy_iterator().next()[0][0].max(), 
    data.as_numpy_iterator().next()[0][0].min())
(256, 256, 3) 1.0 0.0

Splitting Dataset¶

In [11]:
len(data)
Out[11]:
233
In [12]:
train_size = int(len(data)*0.7)
val_size = int(len(data)*0.2)+1
test_size = int(len(data)*0.1)

train_size + val_size + test_size
Out[12]:
233
In [13]:
train_data = data.take(train_size)
val_data = data.skip(train_size).take(val_size)
test_data = data.skip(train_size+val_size).take(test_size)

Training CNN Model¶

In [14]:
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout
from tensorflow.keras.regularizers import l2
In [15]:
model = Sequential()

model.add(Conv2D(16, (3,3), 1, activation='relu', input_shape=(256,256,3)))
model.add(MaxPooling2D())
model.add(Dropout(0.4))

model.add(Conv2D(32, (3,3), 1, activation='relu'))
model.add(MaxPooling2D())
model.add(Dropout(0.5))

model.add(Conv2D(16, (3,3), 1, activation='relu'))
model.add(MaxPooling2D())
model.add(Dropout(0.3))

model.add(Flatten())

model.add(Dense(256, activation='relu'))
model.add(Dropout(0.3))

model.add(Dense(1, kernel_regularizer=l2(0.001), activation='linear'))

model.compile(optimizer='adam', loss='hinge', metrics='accuracy')

model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d (Conv2D)             (None, 254, 254, 16)      448       
                                                                 
 max_pooling2d (MaxPooling2D  (None, 127, 127, 16)     0         
 )                                                               
                                                                 
 dropout (Dropout)           (None, 127, 127, 16)      0         
                                                                 
 conv2d_1 (Conv2D)           (None, 125, 125, 32)      4640      
                                                                 
 max_pooling2d_1 (MaxPooling  (None, 62, 62, 32)       0         
 2D)                                                             
                                                                 
 dropout_1 (Dropout)         (None, 62, 62, 32)        0         
                                                                 
 conv2d_2 (Conv2D)           (None, 60, 60, 16)        4624      
                                                                 
 max_pooling2d_2 (MaxPooling  (None, 30, 30, 16)       0         
 2D)                                                             
                                                                 
 dropout_2 (Dropout)         (None, 30, 30, 16)        0         
                                                                 
 flatten (Flatten)           (None, 14400)             0         
                                                                 
 dense (Dense)               (None, 256)               3686656   
                                                                 
 dropout_3 (Dropout)         (None, 256)               0         
                                                                 
 dense_1 (Dense)             (None, 1)                 257       
                                                                 
=================================================================
Total params: 3,696,625
Trainable params: 3,696,625
Non-trainable params: 0
_________________________________________________________________
In [16]:
logdir = 'LogDir'

try:
    os.mkdir(logdir)
except Exception as e:
    pass    

tfb_callback = tf.keras.callbacks.TensorBoard(logdir)
In [17]:
model_fit = model.fit(train_data, epochs=30, shuffle=True, validation_data=val_data, callbacks=[tfb_callback])
Epoch 1/30
163/163 [==============================] - 13s 50ms/step - loss: 0.6927 - accuracy: 0.6919 - val_loss: 0.5327 - val_accuracy: 0.6257
Epoch 2/30
163/163 [==============================] - 8s 48ms/step - loss: 0.3438 - accuracy: 0.8507 - val_loss: 0.4775 - val_accuracy: 0.6915
Epoch 3/30
163/163 [==============================] - 8s 48ms/step - loss: 0.2835 - accuracy: 0.8765 - val_loss: 0.4175 - val_accuracy: 0.7759
Epoch 4/30
163/163 [==============================] - 8s 47ms/step - loss: 0.2550 - accuracy: 0.8878 - val_loss: 0.3359 - val_accuracy: 0.8145
Epoch 5/30
163/163 [==============================] - 8s 48ms/step - loss: 0.2673 - accuracy: 0.8892 - val_loss: 0.2837 - val_accuracy: 0.8690
Epoch 6/30
163/163 [==============================] - 8s 48ms/step - loss: 0.2396 - accuracy: 0.8969 - val_loss: 0.3367 - val_accuracy: 0.8344
Epoch 7/30
163/163 [==============================] - 8s 50ms/step - loss: 0.2333 - accuracy: 0.8984 - val_loss: 0.3092 - val_accuracy: 0.8890
Epoch 8/30
163/163 [==============================] - 9s 52ms/step - loss: 0.2239 - accuracy: 0.9016 - val_loss: 0.2911 - val_accuracy: 0.8816
Epoch 9/30
163/163 [==============================] - 9s 55ms/step - loss: 0.2101 - accuracy: 0.9103 - val_loss: 0.2712 - val_accuracy: 0.8896
Epoch 10/30
163/163 [==============================] - 14s 88ms/step - loss: 0.2069 - accuracy: 0.9112 - val_loss: 0.2333 - val_accuracy: 0.8923
Epoch 11/30
163/163 [==============================] - 15s 90ms/step - loss: 0.1890 - accuracy: 0.9206 - val_loss: 0.2094 - val_accuracy: 0.9156
Epoch 12/30
163/163 [==============================] - 15s 90ms/step - loss: 0.1913 - accuracy: 0.9233 - val_loss: 0.2221 - val_accuracy: 0.8963
Epoch 13/30
163/163 [==============================] - 15s 91ms/step - loss: 0.1901 - accuracy: 0.9195 - val_loss: 0.1773 - val_accuracy: 0.9315
Epoch 14/30
163/163 [==============================] - 14s 85ms/step - loss: 0.1777 - accuracy: 0.9243 - val_loss: 0.2022 - val_accuracy: 0.8996
Epoch 15/30
163/163 [==============================] - 8s 47ms/step - loss: 0.1825 - accuracy: 0.9245 - val_loss: 0.2150 - val_accuracy: 0.8730
Epoch 16/30
163/163 [==============================] - 8s 47ms/step - loss: 0.1667 - accuracy: 0.9291 - val_loss: 0.1533 - val_accuracy: 0.9242
Epoch 17/30
163/163 [==============================] - 8s 49ms/step - loss: 0.1585 - accuracy: 0.9321 - val_loss: 0.2060 - val_accuracy: 0.9289
Epoch 18/30
163/163 [==============================] - 8s 48ms/step - loss: 0.1579 - accuracy: 0.9323 - val_loss: 0.1934 - val_accuracy: 0.8896
Epoch 19/30
163/163 [==============================] - 8s 47ms/step - loss: 0.1512 - accuracy: 0.9344 - val_loss: 0.1801 - val_accuracy: 0.9116
Epoch 20/30
163/163 [==============================] - 8s 47ms/step - loss: 0.1443 - accuracy: 0.9388 - val_loss: 0.1515 - val_accuracy: 0.9435
Epoch 21/30
163/163 [==============================] - 8s 47ms/step - loss: 0.1373 - accuracy: 0.9421 - val_loss: 0.1550 - val_accuracy: 0.9222
Epoch 22/30
163/163 [==============================] - 8s 48ms/step - loss: 0.1418 - accuracy: 0.9408 - val_loss: 0.1303 - val_accuracy: 0.9342
Epoch 23/30
163/163 [==============================] - 8s 47ms/step - loss: 0.1148 - accuracy: 0.9513 - val_loss: 0.1598 - val_accuracy: 0.9262
Epoch 24/30
163/163 [==============================] - 8s 48ms/step - loss: 0.1183 - accuracy: 0.9538 - val_loss: 0.1371 - val_accuracy: 0.9368
Epoch 25/30
163/163 [==============================] - 8s 47ms/step - loss: 0.1135 - accuracy: 0.9519 - val_loss: 0.1417 - val_accuracy: 0.9242
Epoch 26/30
163/163 [==============================] - 8s 48ms/step - loss: 0.1145 - accuracy: 0.9513 - val_loss: 0.1525 - val_accuracy: 0.9069
Epoch 27/30
163/163 [==============================] - 8s 50ms/step - loss: 0.1160 - accuracy: 0.9498 - val_loss: 0.1439 - val_accuracy: 0.9355
Epoch 28/30
163/163 [==============================] - 8s 47ms/step - loss: 0.0992 - accuracy: 0.9588 - val_loss: 0.1409 - val_accuracy: 0.9235
Epoch 29/30
163/163 [==============================] - 8s 47ms/step - loss: 0.0973 - accuracy: 0.9590 - val_loss: 0.1334 - val_accuracy: 0.9275
Epoch 30/30
163/163 [==============================] - 8s 50ms/step - loss: 0.1002 - accuracy: 0.9586 - val_loss: 0.1911 - val_accuracy: 0.8870

Model Performance¶

In [18]:
fig, axs = plt.subplots(2,1, figsize=(8,8))
axs[0].plot(model_fit.history['loss'], color='red', label='Loss')
axs[0].plot(model_fit.history['val_loss'], color='orange', label='Val_Loss')
axs[0].legend(loc='upper right')


axs[1].plot(model_fit.history['accuracy'], color='green', label='Accuracy')
axs[1].plot(model_fit.history['val_accuracy'], color='teal', label='Val_Accuracy')
axs[1].legend(loc='upper left')
Out[18]:
<matplotlib.legend.Legend at 0x1d9b3307940>
No description has been provided for this image

Evaluating Model¶

In [19]:
from tensorflow.keras.metrics import Precision, Recall, BinaryAccuracy, SpecificityAtSensitivity
In [20]:
pre = Precision()
rec = Recall()
acc = BinaryAccuracy()
spe = SpecificityAtSensitivity(0.5)
In [21]:
for batch in test_data.as_numpy_iterator():
    X, y = batch
    y_pred = model.predict(X)
    pre.update_state(y, y_pred)
    rec.update_state(y, y_pred)
    acc.update_state(y, y_pred)
    spe.update_state(y, y_pred)
1/1 [==============================] - 0s 184ms/step
1/1 [==============================] - 0s 41ms/step
1/1 [==============================] - 0s 44ms/step
1/1 [==============================] - 0s 48ms/step
1/1 [==============================] - 0s 43ms/step
1/1 [==============================] - 0s 46ms/step
1/1 [==============================] - 0s 45ms/step
1/1 [==============================] - 0s 44ms/step
1/1 [==============================] - 0s 39ms/step
1/1 [==============================] - 0s 40ms/step
1/1 [==============================] - 0s 35ms/step
1/1 [==============================] - 0s 31ms/step
1/1 [==============================] - 0s 30ms/step
1/1 [==============================] - 0s 31ms/step
1/1 [==============================] - 0s 34ms/step
1/1 [==============================] - 0s 34ms/step
1/1 [==============================] - 0s 33ms/step
1/1 [==============================] - 0s 31ms/step
1/1 [==============================] - 0s 35ms/step
1/1 [==============================] - 0s 33ms/step
1/1 [==============================] - 0s 34ms/step
1/1 [==============================] - 0s 37ms/step
1/1 [==============================] - 0s 231ms/step
In [22]:
precision = pre.result().numpy()
recall = rec.result().numpy()
f1 = 2 * (precision*recall) / (precision+recall)

print('Accuracy\t:', acc.result().numpy())
print('Precision\t:', precision)
print('Recall\t\t:', recall)
print('Specificity\t:', spe.result().numpy())
print('F1\t\t:', f1)
Accuracy	: 0.8926081
Precision	: 0.98275864
Recall		: 0.79831934
Specificity	: 0.99444443
F1		: 0.8809892264552719

Exporting Model¶

In [23]:
model.save(os.path.join('models','ODIR_SVM.h5'))
In [24]:
from tensorflow.keras.models import load_model
In [25]:
new_model = load_model(os.path.join('models','ODIR_SVM.h5'))
new_model.summary()
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d (Conv2D)             (None, 254, 254, 16)      448       
                                                                 
 max_pooling2d (MaxPooling2D  (None, 127, 127, 16)     0         
 )                                                               
                                                                 
 dropout (Dropout)           (None, 127, 127, 16)      0         
                                                                 
 conv2d_1 (Conv2D)           (None, 125, 125, 32)      4640      
                                                                 
 max_pooling2d_1 (MaxPooling  (None, 62, 62, 32)       0         
 2D)                                                             
                                                                 
 dropout_1 (Dropout)         (None, 62, 62, 32)        0         
                                                                 
 conv2d_2 (Conv2D)           (None, 60, 60, 16)        4624      
                                                                 
 max_pooling2d_2 (MaxPooling  (None, 30, 30, 16)       0         
 2D)                                                             
                                                                 
 dropout_2 (Dropout)         (None, 30, 30, 16)        0         
                                                                 
 flatten (Flatten)           (None, 14400)             0         
                                                                 
 dense (Dense)               (None, 256)               3686656   
                                                                 
 dropout_3 (Dropout)         (None, 256)               0         
                                                                 
 dense_1 (Dense)             (None, 1)                 257       
                                                                 
=================================================================
Total params: 3,696,625
Trainable params: 3,696,625
Non-trainable params: 0
_________________________________________________________________